__init__(self, world: Optional[Simulator], zmq_ip: str = "127.0.0.1", zmq_port: str = "19982") -> Nonestop(self) -> None_parse_and_execute(self, command: str) -> Optional[str]_handle_call_elevator(self, floor: int, direction: str) -> Dict[str, Any]_handle_select_floor(self, floor: int, elevator_id: int) -> Dict[str, Any]_handle_open_door(self, elevator_id: int) -> Dict[str, Any]_handle_close_door(self, elevator_id: int) -> Dict[str, Any]send_door_opened_message(self, elevator_id: int) -> Nonesend_door_closed_message(self, elevator_id: int) -> Nonesend_floor_arrived_message(self, elevator_id: int, floor: int, direction: Optional[MoveDirection]) -> Nonefetch_states(self) -> List[Dict[str, Any]]set_world(self, world: Simulator) -> Nonestop(self) -> Noneui_call_elevator(self, data: Dict[str, Any]) -> strui_select_floor(self, data: Dict[str, Any]) -> strui_open_door(self, data: Dict[str, Any]) -> strui_close_door(self, data: Dict[str, Any]) -> str__init__(self, world: Simulator, api: ElevatorAPI) -> Noneadd_call(self, floor: int, direction: str) -> Noneadd_outside_call(self, floor: int, direction: Optional[MoveDirection]) -> strassign_task(self, elevator_idx: int, floor: int, call_id: Optional[str] = None) -> Noneget_call_direction(self, call_id: str) -> Optional[MoveDirection]complete_call(self, call_id: str) -> None_process_pending_calls(self) -> None_optimize_task_queue(self, elevator: Elevator) -> None_get_elevator_committed_direction(self, elevator: Elevator) -> Optional[MoveDirection]_can_elevator_serve_call(self, elevator: Elevator, floor: int, direction: Optional[MoveDirection]) -> bool__init__(self, elevator_id: int, world: Simulator, api: ElevatorAPI) -> Noneupdate(self) -> Nonerequest_movement_if_needed(self) -> None_set_floor(self, new_floor: int) -> None_set_moving_state(self, direction_value: str) -> None_is_moving(self) -> bool_get_movement_direction(self) -> intopen_door(self) -> Noneclose_door(self) -> None_determine_direction(self) -> Nonecalculate_estimated_time(self, floor: int, direction: Optional[MoveDirection]) -> float_handle_arrival_at_target_floor(self, current_time: float) -> Nonereset(self) -> NoneThe Elevator Simulation System is a multi-elevator control system that manages the operation of two elevators serving floors -1, 1, 2, and 3 (note: floor 0 does not exist). The system handles user requests for calling elevators from floors and selecting destination floors from within elevators, implementing efficient dispatching algorithms to optimize service time.
The system follows a layered architecture with clear separation of concerns:
classDiagram class Simulator { +Optional~ElevatorAPI~ api +List~Elevator~ elevators +Optional~Dispatcher~ dispatcher +__init__() void +set_api_and_initialize_components(api: ElevatorAPI) void +update() void +reset() void } class ElevatorAPI { +Optional~Simulator~ world +ZmqClientThread zmq_client +__init__(world: Optional~Simulator~, zmq_ip: str, zmq_port: str) void +set_world(world: Simulator) void +stop() void +_parse_and_execute(command: str) Optional~str~ +_handle_call_elevator(floor: int, direction: str) Dict~str, Any~ +_handle_select_floor(floor: int, elevator_id: int) Dict~str, Any~ +_handle_open_door(elevator_id: int) Dict~str, Any~ +_handle_close_door(elevator_id: int) Dict~str, Any~ +_handle_reset() Dict~str, Any~ +send_door_opened_message(elevator_id: int) void +send_door_closed_message(elevator_id: int) void +send_floor_arrived_message(elevator_id: int, floor: int, direction: Optional~MoveDirection~) void +fetch_states() List~Dict~str, Any~~ +ui_call_elevator(data: Dict~str, Any~) str +ui_select_floor(data: Dict~str, Any~) str +ui_open_door(data: Dict~str, Any~) str +ui_close_door(data: Dict~str, Any~) str } class Dispatcher { +Simulator world +ElevatorAPI api +Dict~str, Call~ pending_calls +__init__(world: Simulator, api: ElevatorAPI) void +add_call(floor: int, direction: str) void +add_outside_call(floor: int, direction: Optional~MoveDirection~) str +assign_task(elevator_idx: int, floor: int, call_id: Optional~str~) void +get_call_direction(call_id: str) Optional~MoveDirection~ +complete_call(call_id: str) void +update() void +reset() void -_process_pending_calls() void -_optimize_task_queue(elevator: Elevator) void -_get_elevator_committed_direction(elevator: Elevator) Optional~MoveDirection~ -_can_elevator_serve_call(elevator: Elevator, floor: int, direction: Optional~MoveDirection~) bool } class Elevator { +int id +Simulator world +ElevatorAPI api +int current_floor +int previous_floor +List~Task~ task_queue +ElevatorState state +DoorState door_state +Optional~MoveDirection~ direction +float last_state_change +float last_door_change +Optional~float~ moving_since +bool floor_changed +bool floor_arrival_announced +Optional~float~ arrival_time +bool serviced_current_arrival +__init__(elevator_id: int, world: Simulator, api: ElevatorAPI) void +update() void +request_movement_if_needed() void +open_door() void +close_door() void +calculate_estimated_time(floor: int, direction: Optional~MoveDirection~) float +reset() void -_set_floor(new_floor: int) void -_set_moving_state(direction_value: str) void -_is_moving() bool -_get_movement_direction() int -_determine_direction() void -_handle_arrival_at_target_floor(current_time: float) void } class Task { +int floor +Optional~str~ call_id +__init__(floor: int, call_id: Optional~str~) void +is_outside_call() bool } class Call { +str call_id +int floor +Optional~MoveDirection~ direction +CallState state +Optional~int~ assigned_elevator +__init__(call_id: str, floor: int, direction: Optional~MoveDirection~) void +assign_to_elevator(elevator_idx: int) void +complete() void +is_pending() bool +is_assigned() bool +is_completed() bool } class ElevatorState { <<enumeration>> IDLE MOVING_UP MOVING_DOWN } class DoorState { <<enumeration>> OPEN CLOSED OPENING CLOSING } class MoveDirection { <<enumeration>> UP DOWN } class CallState { <<enumeration>> PENDING ASSIGNED COMPLETED } Simulator *-- "2" Elevator : manages Simulator *-- "1" Dispatcher : manages Simulator o-- "1" ElevatorAPI : uses Dispatcher o-- Simulator : world reference Dispatcher o-- ElevatorAPI : api reference Dispatcher *-- "*" Call : manages Elevator o-- Simulator : world reference Elevator o-- ElevatorAPI : api reference Elevator *-- "*" Task : task_queue Elevator --> ElevatorState : state Elevator --> DoorState : door_state Elevator --> MoveDirection : direction Task --> Call : references via call_id Call --> MoveDirection : direction Call --> CallState : state ElevatorAPI o-- Simulator : world reference
The central orchestrator that manages all simulation entities and coordinates the main update loop.
Central interface handling external ZMQ communication and delegating commands to appropriate components.
Manages elevator call assignment using efficiency algorithms and optimizes task queues for each elevator.
Represents individual elevator units with autonomous operation including movement, door control, and task processing.
__init__(self) -> NoneFunctional Description: Initializes the simulator with empty references. Components are initialized later via set_api_and_initialize_components.
set_api_and_initialize_components(self, api: ElevatorAPI) -> NoneFunctional Description: Sets the ElevatorAPI instance and initializes dependent components (elevators and dispatcher) that require API access for communication.
update(self) -> NoneFunctional Description: Main update loop that processes the simulation state by updating all elevators and the dispatcher.
reset(self) -> NoneFunctional Description: Resets all simulation components to their initial state.
__init__(self, world: Optional[Simulator], zmq_ip: str = "127.0.0.1", zmq_port: str = "19982") -> NoneFunctional Description: Initializes the API with ZMQ communication thread and world reference.
stop(self) -> NoneFunctional Description: Stops the ZMQ communication thread and performs any necessary cleanup.
_parse_and_execute(self, command: str) -> Optional[str]Functional Description: Parses incoming ZMQ commands and delegates to appropriate handler methods.
call_up@floor, call_down@floor, select_floor@floor#elevator_id, open_door@elevator_id (e.g., open_door@1 or open_door@#1), close_door@elevator_id (e.g., close_door@1 or close_door@#1), resetopen_door and close_door operations, it returns a confirmation message (e.g., door_opened#elevator_id). For other successful operations like calls, floor selections, or reset, it typically returns None if no error occurred that would prevent a standard ZMQ confirmation (if any is expected beyond the action itself)._handle_call_elevator(self, floor: int, direction: str) -> Dict[str, Any]Functional Description: Processes elevator call requests from floors by delegating to dispatcher.
_handle_select_floor(self, floor: int, elevator_id: int) -> Dict[str, Any]Functional Description: Processes floor selection requests from within elevators.
_handle_open_door(self, elevator_id: int) -> Dict[str, Any]Functional Description: Manually opens elevator doors if conditions allow.
_handle_close_door(self, elevator_id: int) -> Dict[str, Any]Functional Description: Manually closes elevator doors if conditions allow.
send_door_opened_message(self, elevator_id: int) -> NoneFunctional Description: Sends door_opened#elevator_id message via ZMQ.
send_door_closed_message(self, elevator_id: int) -> NoneFunctional Description: Sends door_closed#elevator_id message via ZMQ.
send_floor_arrived_message(self, elevator_id: int, floor: int, direction: Optional[MoveDirection]) -> NoneFunctional Description: Sends floor arrival messages with appropriate direction prefix.
{direction_prefix}floor_arrived@{floor}#{elevator_id}fetch_states(self) -> List[Dict[str, Any]]Functional Description: Returns current state of all elevators for frontend synchronization. Each dictionary in the list represents an elevator and contains keys such as elevator_id, floor, state (e.g., "IDLE", "MOVING_UP"), door_state (e.g., "OPEN", "CLOSED"), direction (e.g., "UP", "DOWN", "none"), target_floors (a list of floor numbers), and target_floors_origin (a dictionary mapping floor numbers to "outside" or "inside").
set_world(self, world: Simulator) -> NoneFunctional Description: Updates the world reference in the API instance. This is useful if the Simulator instance is not available at the time of ElevatorAPI initialization.
stop(self) -> NoneFunctional Description: Stops the ZMQ client thread gracefully, ensuring all connections are closed and resources are released. This is typically called during application shutdown.
ui_call_elevator(self, data: Dict[str, Any]) -> strFunctional Description: Handles 'call elevator' requests originating from the UI (likely via WebSocketBridge). Parses floor and direction from the data dictionary and delegates to the internal _handle_call_elevator method. Returns a JSON string indicating the outcome of the operation (success or error).
ui_select_floor(self, data: Dict[str, Any]) -> strFunctional Description: Handles 'select floor' requests originating from the UI. Parses floor and elevatorId from the data dictionary and delegates to the internal _handle_select_floor method. Returns a JSON string indicating the outcome.
ui_open_door(self, data: Dict[str, Any]) -> strFunctional Description: Handles 'open door' requests originating from the UI. Parses elevatorId from the data dictionary and delegates to the internal _handle_open_door method. Returns a JSON string indicating the outcome.
ui_close_door(self, data: Dict[str, Any]) -> strFunctional Description: Handles 'close door' requests originating from the UI. Parses elevatorId from the data dictionary and delegates to the internal _handle_close_door method. Returns a JSON string indicating the outcome.
__init__(self, world: Simulator, api: ElevatorAPI) -> NoneFunctional Description: Initializes dispatcher with world and API references and empty pending calls dictionary.
add_call(self, floor: int, direction: str) -> NoneFunctional Description: Adds an outside call request and processes pending calls queue.
add_outside_call(self, floor: int, direction: Optional[MoveDirection]) -> strFunctional Description: Creates a new Call object with unique ID and adds to pending calls.
assign_task(self, elevator_idx: int, floor: int, call_id: Optional[str] = None) -> NoneFunctional Description: Assigns a task to a specific elevator, handling both outside calls (with call_id) and inside calls.
Supporting UML Sequence Diagram:
sequenceDiagram participant API as ElevatorAPI participant D as Dispatcher participant E as Elevator participant ZMQ as ZMQ Client API->>D: assign_task(elevator_idx, floor, call_id) D->>E: Check current_floor and door_state alt Already at floor with closed doors D->>API: send_floor_arrived_message() API->>ZMQ: floor_arrived message D->>E: open_door() D->>D: complete_call(call_id) else Need to add task D->>D: Check for duplicates D->>E: task_queue.append(Task) D->>D: _optimize_task_queue() alt Door is open D->>E: close_door() else Door is closed D->>E: request_movement_if_needed() end end
get_call_direction(self, call_id: str) -> Optional[MoveDirection]Functional Description: Retrieves the direction for a pending call by call_id.
complete_call(self, call_id: str) -> NoneFunctional Description: Marks a call as completed and removes it from pending calls to free memory.
_process_pending_calls(self) -> NoneFunctional Description: Processes all pending calls by finding suitable elevators and assigning tasks.
_optimize_task_queue(self, elevator: Elevator) -> NoneFunctional Description: Optimizes elevator task queue using SCAN-like algorithm.
Supporting UML Sequence Diagram:
sequenceDiagram participant D as Dispatcher participant E as Elevator D->>E: Get current state and direction D->>D: Separate tasks above/below current floor alt Moving UP D->>D: Sort above tasks ascending D->>D: Sort below tasks ascending D->>E: task_queue = above + below else Moving DOWN D->>D: Sort below tasks descending D->>D: Sort above tasks ascending D->>E: task_queue = below + above else IDLE D->>D: Find closest task alt Closest is above D->>D: Set direction UP, optimize as moving up else Closest is below D->>D: Set direction DOWN, optimize as moving down end end
_get_elevator_committed_direction(self, elevator: Elevator) -> Optional[MoveDirection]Functional Description: Determines the direction an elevator is committed to based on current state and task queue.
_can_elevator_serve_call(self, elevator: Elevator, floor: int, direction: Optional[MoveDirection]) -> boolFunctional Description: Determines if an elevator can serve an outside call.
__init__(self, elevator_id: int, world: Simulator, api: ElevatorAPI) -> NoneFunctional Description: Initializes elevator with ID, world reference, API instance, and default state values.
update(self) -> NoneFunctional Description: Main update method called every simulation cycle to handle elevator operations.
Supporting UML Sequence Diagram:
sequenceDiagram participant E as Elevator participant API as ElevatorAPI participant ZMQ as ZMQ Client loop Every Update Cycle E->>E: Check if floor_changed alt Floor changed E->>E: Set arrival_time, reset flags end alt Is moving E->>E: Check if travel time elapsed alt Time elapsed E->>E: Calculate next_floor (skip floor 0) E->>E: _set_floor(next_floor) end E->>E: Check if arrived at target alt Arrived and announcement delay passed E->>E: _handle_arrival_at_target_floors() E->>API: send_floor_arrived_message() API->>ZMQ: floor_arrived message end else Not moving alt Door state transitions E->>E: Handle OPENING/CLOSING/timeout alt Door opened E->>API: send_door_opened_message() API->>ZMQ: door_opened message else Door closed E->>API: send_door_closed_message() API->>ZMQ: door_closed message E->>E: request_movement_if_needed() end end alt At target floor with closed doors E->>E: open_door() E->>E: Remove task from queue end alt Has remaining tasks E->>E: request_movement_if_needed() end end end
request_movement_if_needed(self) -> NoneFunctional Description: Initiates elevator movement if there are target floors and doors are closed.
_determine_direction()_set_moving_state()_set_floor(self, new_floor: int) -> NoneFunctional Description: Updates elevator's current floor and sets floor_changed flag.
_set_moving_state(self, direction_value: str) -> NoneFunctional Description: Sets elevator's movement state and updates timestamps.
_is_moving(self) -> boolFunctional Description: Returns True if elevator is in MOVING_UP or MOVING_DOWN state.
_get_movement_direction(self) -> intFunctional Description: Returns movement direction as integer (1 for up, -1 for down, 0 for idle).
open_door(self) -> NoneFunctional Description: Opens elevator doors if not already open/closing and not moving.
close_door(self) -> NoneFunctional Description: Closes elevator doors if not already closed/opening and not moving.
_determine_direction(self) -> NoneFunctional Description: Determines optimal movement direction based on task queue.
calculate_estimated_time(self, floor: int, direction: Optional[MoveDirection]) -> floatFunctional Description: Calculates estimated time to serve a floor request considering current state and existing tasks.
Supporting UML Sequence Diagram:
sequenceDiagram participant D as Dispatcher participant E as Elevator D->>E: calculate_estimated_time(floor, direction) alt Already at floor with open doors E->>D: return 0 else E->>E: Calculate door closing time if needed alt Elevator is IDLE E->>E: Calculate direct travel time E->>E: Add door operation time E->>D: return total_time else Elevator is moving E->>E: Simulate serving existing tasks E->>E: Check if target reached during simulation alt Target reached with correct direction E->>D: return simulated_time else Target not reached E->>E: Continue simulation to completion E->>E: Add travel time to target E->>D: return total_time end end end
_handle_arrival_at_target_floor(self, current_time: float) -> NoneFunctional Description: Handles logic when elevator arrives at a target floor.
reset(self) -> NoneFunctional Description: Resets elevator to initial state (floor 1, idle, doors closed, empty task queue).
__init__(self, floor: int, call_id: Optional[str]) -> NoneFunctional Description: Initializes a task with target floor and optional call ID for outside calls.
is_outside_call(self) -> boolFunctional Description: Returns True if task represents an outside call (has call_id), False for inside calls.
__init__(self, call_id: str, floor: int, direction: Optional[MoveDirection]) -> NoneFunctional Description: Initializes a call with unique ID, target floor, and direction.
assign_to_elevator(self, elevator_idx: int) -> NoneFunctional Description: Assigns call to specified elevator and updates state to ASSIGNED.
complete(self) -> NoneFunctional Description: Marks call as completed.
is_pending(self) -> boolFunctional Description: Returns True if call state is PENDING.
is_assigned(self) -> boolFunctional Description: Returns True if call state is ASSIGNED.
is_completed(self) -> boolFunctional Description: Returns True if call state is COMPLETED.
This part here will explain the implementation and click event of floor button in detail.
Dispatcher will organize the optimal route to stop at all target floors.Once being clicked, the floor button is considered 'activated' in red and will not respond to further clicking until the elevator has reached the target floor and the button turns back to the 'idle' in blue.
User Operation: "select_floor": ["-1#1", "-1#2", "1#1", "1#2", "2#1", "2#2", "3#1", "3#2"]
select_floor@i#k means a user in elevator #k selects to go to the i-th floor.
Corresponding System Events: "floor_arrived":["up","down",""],["-1","1","2","3"],["#1", "#2"]
"up_floor_i_arrived#k", indicating that elevator #k has arrived at the i-th floor while moving upwards. "floor_i_arrived#k",indicating that elevator #k has stopped at the i-th floor.
This part here will explain the implementation and click event of call up/down button in detail.
Same as floor button, the call up/down button is 'activated' in red and will not respond until the elevator has arrived at the passenger's floor, the button will then turn back to 'idle' in blue.
User Operations: "call_down": ["3", "2", "1"] "call_up": ["-1", "1", "2"]
call_up/call_down@i signifies the user at floor i pressing the button to call the elevator to go upwards/downwards.
This part here will explain the implementation and click event of door open/close button in detail.
S3.1.1 Open Button:
S3.1.2 Close Button:
The specific click event of the Open/Close button will be presented in the UML sequence diagram below.
User Operations: "open_door": ["#1", "#2"] "close_door": ["#1", "#2"] open_door/close_door#i means open/close the doors of elevator #i
Corresponding System Events "door_opened": ["#1", "#2"] "door_closed": ["#1", "#2"]
door_opened/door_closed#i means the doors of elevator #i have opened/closed
This section describes how the Dispatcher efficiently manages elevator operations, handling user requests and assigning them to the most suitable elevator based on real-time conditions.
The Dispatcher is responsible for managing and assigning outside floor calls (e.g., call up/down from a landing) to the most suitable elevator. It calculates estimated service times and uses this to assign calls. For all assigned tasks, including internal floor selections made by passengers inside an elevator, the Dispatcher optimizes the elevator's task queue to ensure an efficient travel path. Manual door open/close requests, however, are handled directly by the Elevator objects (via the ElevatorAPI) and do not involve the Dispatcher's assignment or optimization logic.
When a passenger calls an elevator from a floor:
Dispatcher calculates an estimated service time for each elevator.The Dispatcher employs a modified SCAN algorithm (elevator algorithm) to minimize wait times and reduce unnecessary direction changes:
This section will show how each elevator's state is updated in real-time, managing movement progress, door operations, and transitions to ensure they stay in sync with user interactions and operational logic.
The Elevator object's update method, called periodically by the Simulator, manages the elevator's movement. It tracks the time elapsed since movement started and, once a single-floor travel time is complete, updates the elevator's current floor. This process ensures the elevator's position and movement status are accurately reflected in the simulation.
| Command/Event | Description/When Emitted |
|---|---|
| select_floor@i#k | User in elevator #k selects floor i |
| call_up@i | User at floor i calls elevator up |
| call_down@i | User at floor i calls elevator down |
| open_door#k | User requests to open door of elevator #k |
| close_door#k | User requests to close door of elevator #k |
| reset | System reset to initial state |
| floor_arrived@i#k | Elevator #k arrived at floor i |
| up_floor_arrived@i#k | Elevator #k arrived at floor i moving up |
| down_floor_arrived@i#k | Elevator #k arrived at floor i moving down |
| door_opened#k | Elevator #k door opened |
| door_closed#k | Elevator #k door closed |